package com.tyk.bookstore.back.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.common.core.util.TreeUtil;
import com.tyk.bookstore.back.system.model.entity.MenuDo;
import com.tyk.bookstore.back.system.model.query.MenuAddQuery;
import com.tyk.bookstore.back.system.model.query.MenuListQuery;
import com.tyk.bookstore.back.system.model.query.MenuUpdateQuery;
import com.tyk.bookstore.back.system.model.result.MenuResultEnum;
import com.tyk.bookstore.back.system.model.type.MenuType;
import com.tyk.bookstore.back.system.model.vo.MenuListVo;
import com.tyk.bookstore.back.system.model.vo.MenuSuggestionVo;
import com.tyk.bookstore.back.system.model.vo.MenuVo;
import com.tyk.bookstore.back.system.repository.MenuRepository;
import com.tyk.bookstore.back.system.repository.RoleMenuRepository;
import com.tyk.bookstore.back.system.service.MenuService;
import com.tyk.bookstore.back.system.util.MapstructSystem;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;
import java.util.Objects;

@RequiredArgsConstructor
@Slf4j
@Service
public class MenuServiceImpl implements MenuService {

    private final MapstructSystem mapstructSystem;
    private final TransactionTemplate transactionTemplate;
    private final MenuRepository menuRepository;
    private final RoleMenuRepository roleMenuRepository;

    @Override
    public List<MenuSuggestionVo> getSuggestionList(Boolean showMenuItem) {
        LambdaQueryWrapper<MenuDo> w = Wrappers.lambdaQuery(MenuDo.class);
        w.select(MenuDo::getId, MenuDo::getTitle, MenuDo::getParentId)
                .ne(!showMenuItem, MenuDo::getType, MenuType.MENU_ITEM);
        List<MenuDo> menuDoList = menuRepository.selectList(w);
        return TreeUtil.buildTree(
                menuDoList,
                MenuDo::getId,
                MenuDo::getParentId,
                (item) -> new MenuSuggestionVo().setId(String.valueOf(item.getId())).setName(item.getTitle()),
                MenuSuggestionVo::addChild
        );
    }

    @Override
    public List<MenuListVo> getList(MenuListQuery query) {
        LambdaQueryWrapper<MenuDo> w = buildPageQueryWrapper(query);
        List<MenuDo> menuList = menuRepository.selectList(w);
        return TreeUtil.buildTree(
                menuList,
                MenuDo::getId,
                MenuDo::getParentId,
                mapstructSystem::toGetMenuListVo,
                MenuListVo::addChild
        );
    }


    @Override
    public void add(MenuAddQuery query) {

        // 校验父ID
        checkMenuParentId(query.getParentId());

        // 名称不重复
        if (ObjUtil.notBlank(query.getName())) {
            AssertUtil.isFalse(
                    menuRepository.exist1Eq(
                            MenuDo::getName, query.getName(), MenuDo.class
                    ),
                    MenuResultEnum.MENU_NAME_ALREADY_EXISTS
            );
        }

        // 路径不重复
        AssertUtil.isFalse(
                menuRepository.exist1Eq(
                        MenuDo::getPath, query.getPath(), MenuDo.class
                ),
                MenuResultEnum.MENU_PATH_ALREADY_EXISTS
        );

        // 转换
        MenuDo menu = mapstructSystem.toMenuDo(query);
        menu.setId(IdGenerator.id());
        switch (query.getType()) {
            case SUB_MENU -> menu.setRedirect(query.getRedirect());
            case MENU_ITEM -> menu.setComponent(query.getComponent()).setKeepAlive(query.getKeepAlive());
        }
        menuRepository.insert(menu);
    }

    @Override
    public MenuVo getById(Long id) {
        MenuVo vo = menuRepository.selectGetMenuByIdVo(id);
        AssertUtil.notNull(vo, MenuResultEnum.MENU_NOT_FOUND);
        return vo;
    }

    @Override
    public void updateById(MenuUpdateQuery query) {

        // 菜单存在
        AssertUtil.isTrue(
                menuRepository.exist1Eq(
                        MenuDo::getId, query.getId(), MenuDo.class
                ),
                MenuResultEnum.MENU_NOT_FOUND
        );

        // 校验父级ID
        checkMenuParentId(query.getParentId());

        // 名称不重复
        if (ObjUtil.notBlank(query.getName())) {
            AssertUtil.isFalse(
                    menuRepository.exist1Eq1Ne(
                            MenuDo::getName, query.getName(),
                            MenuDo::getId, query.getId(),
                            MenuDo.class
                    ),
                    MenuResultEnum.MENU_NAME_ALREADY_EXISTS
            );
        }

        // 路径不重复
        AssertUtil.isFalse(
                menuRepository.exist1Eq1Ne(
                        MenuDo::getPath, query.getPath(),
                        MenuDo::getId, query.getId(),
                        MenuDo.class
                ),
                MenuResultEnum.MENU_PATH_ALREADY_EXISTS
        );

        // 更新
        MenuDo menu = mapstructSystem.toMenuDo(query);
        switch (query.getType()) {
            case SUB_MENU -> menu.setRedirect(query.getRedirect());
            case MENU_ITEM -> menu.setComponent(query.getComponent()).setKeepAlive(query.getKeepAlive());
        }
        menuRepository.updateById(menu);

    }

    @Override
    public void deleteById(Long id) {
        AssertUtil.isTrue(
                menuRepository.exist1Eq(
                        MenuDo::getId, id, MenuDo.class
                ),
                MenuResultEnum.MENU_NOT_FOUND
        );
        transactionTemplate.executeWithoutResult(status -> {
            menuRepository.deleteById(id);
            roleMenuRepository.deleteByMenuId(id);
        });
    }

    /**
     * 校验父菜单ID的合法性
     *
     * @param parentId 父菜单ID
     */
    private void checkMenuParentId(Long parentId) {
        if (!Objects.equals(MenuDo.TOP_ID, parentId)) {
            AssertUtil.isTrue(
                    menuRepository.exist2Eq(
                            MenuDo::getId, parentId,
                            MenuDo::getType, MenuType.SUB_MENU,
                            MenuDo.class
                    ),
                    MenuResultEnum.MENU_PARENT_NOT_FOUND
            );
        }
    }

    /**
     * 构建分页查询Wrapper
     *
     * @param query 查询参数
     * @return Wrapper
     */
    private LambdaQueryWrapper<MenuDo> buildPageQueryWrapper(MenuListQuery query) {
        LambdaQueryWrapper<MenuDo> w = Wrappers.lambdaQuery(MenuDo.class);

        w.eq(query.getParentId() != null, MenuDo::getParentId, query.getParentId());

        w.eq(query.getType() != null, MenuDo::getType, query.getType());

        w.eq(query.getHidden() != null, MenuDo::getHidden, query.getHidden());

        w.eq(query.getKeepAlive() != null, MenuDo::getKeepAlive, query.getKeepAlive());

        w.like(ObjUtil.notBlank(query.getTitle()), MenuDo::getTitle, query.getTitle());

        w.ge(query.getStartTime() != null, MenuDo::getCreateTime, query.getStartTime());
        w.le(query.getEndTime() != null, MenuDo::getCreateTime, query.getEndTime());

        return w;
    }

}
